import utils from './utils';
import { danmakuType } from '../enums/danmakuType';
import danmaku, { position } from '../danmaku/danmaku';
import logger from '../core/logger';

export default class ssSubtitle {
  private danmaku: danmaku;
  private bottomSubtitles: Map<number, number>;
  private topSubtitles: Map<number, number>;
  private videoWidth: number;
  private videoHeight: number;
  private baseFontSize: number;
  private lineCount: number;
  private bottomMargin: number;
  private tuneSeconds: number;
  private textLength: number;
  private startTime: number;
  private endTime: number;
  private fontSize: number;
  private position: position;
  private styledText: string;

  constructor(danmaku: danmaku, bottomSubtitles: Map<number, number>, topSubtitles: Map<number, number>, videoWidth: number, videoHeight: number, baseFontSize: number,
              lineCount: number, bottomMargin: number, tuneSeconds: number) {
    if (!utils.isEmpty(danmaku)) {
      this.danmaku = danmaku;
      this.bottomSubtitles = bottomSubtitles;
      this.topSubtitles = topSubtitles;
      this.videoWidth = videoWidth;
      this.videoHeight = videoHeight;
      this.baseFontSize = baseFontSize;
      this.lineCount = lineCount;
      this.bottomMargin = bottomMargin;
      this.tuneSeconds = tuneSeconds;
      this.textLength = danmaku.text.length;
      this.startTime = danmaku.time;
      this.endTime = this.getEndTime(danmaku.time, this.tuneSeconds);
      this.fontSize = danmaku.textSize - 25 + baseFontSize;
      this.position = this.getPosition();
      this.styledText = this.getStyledText();
    }
  }

  toString() {
    let start = this.format(utils.formatTime(this.startTime));
    let end = this.format(utils.formatTime(this.endTime));
    return `Dialogue: 3,${start},${end},AcplayDefault,,0000,0000,0000,,${this.styledText}`;
  }

  private format(s: string): string {
    try {
      if (!s.includes('.'))
        return s += ".00";
      if (s.substring(s.lastIndexOf('.')).length == 2)
        return s + "0";
      if (s.substring(s.lastIndexOf('.')).length > 3) {
        let index = s.lastIndexOf('.');
        return s.slice(index + 3, s.length - index - 3);
      }
    } catch (e) {
      logger.error(e);
    }
    return s;
  }

  private getStyledText() {
    try {
      let colourMarkup, borderMarkup, fontSizeMarkup, styleMarkup;
      if (this.danmaku.textColor == 16777215) colourMarkup = "";
      else colourMarkup = `\\c&H${utils.toHexString(this.danmaku.textColor)}`;
      if (this.needWhiteBorder(this.danmaku)) borderMarkup = "\\3c&HFFFFFF";
      else borderMarkup = "";
      if (this.fontSize == this.baseFontSize) fontSizeMarkup = "";
      else fontSizeMarkup = `\\fs${this.fontSize}`;
      if (this.danmaku.danmakuType == danmakuType.normal || this.danmaku.danmakuType == danmakuType.normal2
      || this.danmaku.danmakuType == danmakuType.normal3 || this.danmaku.danmakuType == danmakuType.reverse)
        styleMarkup = `\\move(${this.position.x1},${this.position.y1},${this.position.x2},${this.position.y2})`;
      else
        styleMarkup = `\\a6\\pos(${this.position.x1},${this.position.y1})`;
      return `{{${styleMarkup}${colourMarkup}${borderMarkup}${fontSizeMarkup}}}${this.danmaku.text}`;
    } catch (e) {
      logger.error(e);
      return '';
    }
  }

  private needWhiteBorder(danmaku: danmaku): boolean {
    try {
      let colour = danmaku.textColor;
      if (colour == 0) return true;
      let hls = utils.toHls(utils.toRgb(danmaku.textColor));
      if (hls.h > 30 && hls.h < 210 && hls.l < 33) return true;
      if ((hls.l < 30 || hls.h > 210) && hls.l < 66) return true;
    } catch (e) {
      logger.error(e);
    }
    return false;
  }

  private getPosition(): position {
    let pos = new position();
    try {
      if (this.danmaku.danmakuType == danmakuType.normal || this.danmaku.danmakuType == danmakuType.normal2 || this.danmaku.danmakuType == danmakuType.normal3) {
        pos.x1 = this.videoWidth + this.baseFontSize * this.textLength / 2;
        pos.x2 = -(this.baseFontSize * this.textLength) / 2;
        let y = (this.danmaku.index % this.lineCount + 1) * this.baseFontSize;
        if (y < this.fontSize) y = this.fontSize;
        pos.y1 = pos.y2 = y;
      }
      else if (this.danmaku.danmakuType == danmakuType.bottom) {
        let lineIndex = this.chooseLineCount(this.bottomSubtitles, this.startTime);
        if (this.bottomSubtitles.has(lineIndex))
          this.bottomSubtitles[lineIndex] = this.endTime;
        else
          this.bottomSubtitles.set(lineIndex, this.endTime);
        let x = this.videoWidth / 2;
        let y = this.videoHeight - (this.baseFontSize * lineIndex + this.bottomMargin);
        pos.x1 = pos.x2 = x;
        pos.y1 = pos.y2 = y;
      }
      else if (this.danmaku.danmakuType == danmakuType.top) {
        let lineIndex = this.chooseLineCount(this.topSubtitles, this.startTime);
        if (this.topSubtitles.has(lineIndex))
          this.topSubtitles[lineIndex] = this.endTime;
        else
          this.topSubtitles.set(lineIndex, this.endTime);
        let x = this.videoWidth / 2;
        let y = this.baseFontSize * lineIndex + 1;
        pos.x1 = pos.x2 = x;
        pos.y1 = pos.y2 = y;
      }
      else if (this.danmaku.danmakuType == danmakuType.reverse) {
        pos.x2 = this.videoWidth + this.baseFontSize * this.textLength / 2;
        pos.x1 = -(this.baseFontSize * this.textLength) / 2;
        let y = (this.danmaku.index % this.lineCount + 1) * this.baseFontSize;
        if (y < this.fontSize) y = this.fontSize;
        pos.y1 = pos.y2 = y;
      }
    } catch (e) {
      logger.error(e);
    }
    return pos;
  }

  private chooseLineCount(subtitles: Map<number, number>, startTime: number) {
    let lineIndex: number;
    try {
      const keys: number[] = [...subtitles.keys()];
      let toRemove: Array<number> = [];
      for (let item of subtitles) {
        if (item[1] <= startTime)
          toRemove.push(item[0]);
      }
      toRemove.forEach(s => subtitles.delete(s));
      if (subtitles.size == 0)
        lineIndex = 0;
      else if (subtitles.size == Math.max(...keys))
        lineIndex = Math.min(...keys);
      else {
        lineIndex = 0;
        for (let i = 0; i < subtitles.size; i++) {
          if (!subtitles.has(i)) {
            lineIndex = i;
            break;
          }
        }
        if (lineIndex == 0)
          lineIndex = subtitles.size;
      }
    } catch (e) {
      logger.error(e);
    }
    return lineIndex;
  }

  private getEndTime(showTime: number, offset: number): number {
    try {
      if (this.danmaku.danmakuType == danmakuType.bottom || this.danmaku.danmakuType == danmakuType.top)
        return showTime + 4;
      let endTime;
      if (this.textLength < 5)
        endTime = showTime + 7 + this.textLength / 1.5;
      else if (this.textLength < 12)
        endTime = showTime + 7 + this.textLength / 2;
      else
        endTime = showTime + 13;
      endTime += offset;
      return endTime;
    } catch (e) {
      logger.error(e);
      return 0;
    }
  }
}